import * as d3 from "d3";

export function Simulation(ctx) {

  let context = ctx;

  let simulation = {};

  /**---------------------------------------------------------------------------
   * 初始化受力模型
   */
  this.initSimulation = function() {
    let _this = context;

    simulation = d3.forceSimulation()
      .force("charge", d3.forceManyBody().strength(-5000))
      // .force("collide", d3.forceCollide().strength(-10))
      .force("center", d3.forceCenter(_this.width / 2, _this.height / 2));

    // simulation.force(
    //   "center",
    //   d3.forceCenter(_this.width / 2, _this.height / 2)
    // );
    this.restartNodesSimulation(false);
    this.restartLinksSimulation(true);
  }

  /**---------------------------------------------------------------------------
   * 开始节点受力模型
   * @param nodes
   * @param restart
   */
  this.restartNodesSimulation = function(restart=true) {
    let nodes = context.kgInfo.visibleNodes;
    simulation
      .nodes(nodes)
      .alphaTarget(0)
      .alphaDecay(0.05)
      .on("tick", ticked);
    simulation
      .force("collide", d3.forceCollide().strength(-0.5)
      .radius(function(d) {
        return d.r;
      }))
    if(restart) simulation.alpha(1).restart();
  }

  /**---------------------------------------------------------------------------
   * 开始连接受力模型
   * @param links
   * @param restart
   */
  this.restartLinksSimulation = function(restart=true) {
    let links = context.kgInfo.visibleLinks;
    simulation.force("link", d3.forceLink()
      .distance(function(d) {
        let aveR = d.source.r + d.target.r
        return aveR*aveR*aveR/2000;
      })
      .id(function(d) {
        return d.uuid;
      })
    );
    simulation.force("link").links(links);
    if(restart) simulation.alpha(1).restart();
  }

  /**---------------------------------------------------------------------------
   * 拖动相关回调方法
   */
  this.dragStarted = function() {
    return function(d){
      if (!d3.event.active) simulation.alphaTarget(0.8).restart();
      d.fx = d.x;
      d.fy = d.y;
    }
  }
  this.dragged = function() {
    return function(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
  }
  this.dragEnded = function() {
    return function(d) {
      if (!d3.event.active) simulation.alphaTarget(0);
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
  }

  /**---------------------------------------------------------------------------
   * 受力模型帧回调方法
   */
  function ticked() {
    let _this = context;
    // 更新节点坐标
    _this.graphNode
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });
    // 更新文字坐标
    _this.graphNodeText
      .attr("x", function(d) {
        return d.x;
      })
      .attr("y", function(d) {
        return d.y;
      });
    // 更新连线坐标
    _this.graphLink.attr("d", function(d) {
      let dx = d.target.x - d.source.x,
        dy = d.target.y - d.source.y,
        dr = Math.sqrt(dx * dx + dy * dy),
        unevenCorrection = d.groupLinkUneven ? 0 : 0.2;
      let curvature = 2,
        arc =
          (1.0 / curvature) *
          ((dr * d.groupLinkMaxHalf) /
            (d.groupLinkIndexCorrected - unevenCorrection));
      if (d.groupLinkMiddle) {
        arc = 0;
      }
      let dd =
        "M" +
        d.source.x +
        "," +
        d.source.y +
        "A" +
        arc +
        "," +
        arc +
        " 0 0," +
        d.groupLinkArcDirection +
        " " +
        d.target.x +
        "," +
        d.target.y;
      return dd;
    });
    // 刷新连接线上的文字位置
    _this.graphLinkText
      .attr("x", function(d) {
        if (!d.source.x || !d.target.x) return 0;
        return (parseFloat(d.source.x) + parseFloat(d.target.x)) / 2;
      })
      .attr("y", function(d) {
        if (!d.source.y || !d.target.y) return 0;
        return (parseFloat(d.source.y) + parseFloat(d.target.y)) / 2;
      });
    // 更新节点操作按钮组坐标
    _this.graphNodeButtonGroup
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ") scale(1)";
      });
  }

}